              package com.java.diagnostics.visualizer.displayer.structured;
              
              import com.java.diagnostics.visualizer.data.DataSet;
              import com.java.diagnostics.visualizer.data.StructuredData;
              import com.java.diagnostics.visualizer.data.Variant;
              import com.java.diagnostics.visualizer.data.ids.ID;
              import com.java.diagnostics.visualizer.display.DataDisplayer;
              import com.java.diagnostics.visualizer.exceptions.GCAndMemoryVisualizerException;
              import com.java.diagnostics.visualizer.properties.IDsToDisplayProperties;
              import com.java.diagnostics.visualizer.properties.OutputProperties;
              import java.util.HashSet;
              import java.util.Iterator;
              import java.util.Map;
              import java.util.Map.Entry;
              import java.util.Set;
              import org.eclipse.jface.viewers.ILabelProvider;
              import org.eclipse.jface.viewers.ILabelProviderListener;
              import org.eclipse.jface.viewers.ISelection;
              import org.eclipse.jface.viewers.ITreeContentProvider;
              import org.eclipse.jface.viewers.TreePath;
              import org.eclipse.jface.viewers.TreeSelection;
              import org.eclipse.jface.viewers.TreeViewer;
              import org.eclipse.jface.viewers.Viewer;
              import org.eclipse.jface.viewers.ViewerComparator;
              import org.eclipse.swt.dnd.Clipboard;
              import org.eclipse.swt.dnd.TextTransfer;
              import org.eclipse.swt.dnd.Transfer;
              import org.eclipse.swt.graphics.Image;
              import org.eclipse.swt.layout.GridData;
              import org.eclipse.swt.layout.GridLayout;
              import org.eclipse.swt.widgets.Composite;
              import org.eclipse.swt.widgets.Control;
              import org.eclipse.swt.widgets.Display;
              import org.eclipse.swt.widgets.Label;
              import org.eclipse.swt.widgets.Menu;
              
              public class StructuredDataDisplayer
                implements DataDisplayer
              {
                private TreeViewer tree = null;
                private Composite composite;
              
                public void dispose()
                {
                }
              
                public String[] getFileSaveExtensions()
                {
                  return null;
                }
              
                public String getDefaultFileSaveExtension() {
                  return null;
                }
              
                public boolean isSaveAsDirectory() {
                  return false;
                }
              
                public void print()
                  throws GCAndMemoryVisualizerException
                {
                }
              
                public Object display(DataSet dataSet, OutputProperties outputProperties)
                {
                  this.composite = 
                    ((Composite)outputProperties
                    .getDisplayerProperties(getClass().getName()));
              
                  this.tree = null;
              
                  if (this.composite != null) {
                    Control[] children = this.composite.getChildren();
                    for (int i = 0; i < children.length; i++) {
                      children[i].dispose();
                    }
                    GridLayout layout = new GridLayout();
                    this.composite.setLayout(layout);
              
                    Label label = new Label(this.composite, 0);
                    label.setText(dataSet.getLabel());
              
                    TreeViewer newTree = new TreeViewer(this.composite);
              
                    StructuredTree structuredTree = new StructuredTree(dataSet, 
                      outputProperties);
                    newTree.setContentProvider(structuredTree);
                    newTree.setComparator(new ViewerComparator());
                    newTree.setLabelProvider(new StructuredDataLabelProvider());
              
                    newTree.setInput(structuredTree);
                    newTree.getControl()
                      .setLayoutData(new GridData(1808));
                    newTree.expandToLevel(2);
              
                    newTree.refresh();
              
                    if ((!this.composite.isDisposed()) && (this.composite.getMenu() != null) && 
                      (!this.composite.getMenu().isDisposed())) {
                      newTree.getControl().setMenu(this.composite.getMenu());
                    }
                    this.tree = newTree;
              
                    this.composite.layout();
                  }
                  return this.composite;
                }
              
                public void save(String fileName)
                  throws GCAndMemoryVisualizerException
                {
                }
              
                public void copyToClipboard()
                {
                  if (this.tree == null) {
                    return;
                  }
                  ISelection selection = this.tree.getSelection();
                  StringBuffer data = new StringBuffer();
                  if ((selection instanceof TreeSelection)) {
                    TreeSelection treeSelection = (TreeSelection)selection;
                    TreePath[] paths = treeSelection.getPaths();
                    String lineSeperator = "";
                    int minDepth = 2147483647;
                    for (TreePath path : paths) {
                      int currentDepth = path.getSegmentCount();
              
                      if (path.getLastSegment() == TreePath.EMPTY) {
                        currentDepth = 1;
                      }
                      minDepth = Math.min(minDepth, currentDepth);
                    }
                    for (TreePath path : paths) {
                      Object segment = path.getLastSegment();
              
                      if (segment == TreePath.EMPTY) {
                        segment = path.getFirstSegment();
                      }
                      data.append(lineSeperator);
                      for (int i = 0; i < path.getSegmentCount() - minDepth; i++) {
                        data.append('\t');
                      }
              
                      if ((segment instanceof String))
                        data.append((String)segment);
                      else if ((segment instanceof StructuredTree)) {
                        data.append(
                          ((StructuredTree)path.getLastSegment()).getLabel());
                      }
                      lineSeperator = System.getProperty("line.separator");
                    }
              
                  }
              
                  if (data.length() > 0) {
                    Clipboard clipboard = new Clipboard(Display.getCurrent());
                    TextTransfer transfer = TextTransfer.getInstance();
                    clipboard.setContents(new Object[] { data.toString() }, 
                      new Transfer[] { transfer });
                  }
                }
              
                static class StructuredDataLabelProvider
                  implements ILabelProvider
                {
                  public Image getImage(Object element)
                  {
                    return null;
                  }
              
                  public String getText(Object element)
                  {
                    if ((element instanceof StructuredDataDisplayer.StructuredTree)) {
                      return ((StructuredDataDisplayer.StructuredTree)element).getLabel();
                    }
              
                    return element.toString();
                  }
              
                  public void addListener(ILabelProviderListener listener)
                  {
                  }
              
                  public void dispose()
                  {
                  }
              
                  public boolean isLabelProperty(Object element, String property)
                  {
                    return false;
                  }
              
                  public void removeListener(ILabelProviderListener listener)
                  {
                  }
                }
              
                class StructuredTree
                  implements ITreeContentProvider
                {
                  private Set children;
                  private String label;
                  private Object[] templateArray = new Object[0];
                  private StructuredTree parent;
              
                  private StructuredTree(String label)
                  {
                    this.label = label;
                    this.children = new HashSet();
                  }
              
                  public String getLabel() {
                    return this.label;
                  }
              
                  public Object[] getElements(Object inputElement)
                  {
                    if ((inputElement instanceof StructuredTree)) {
                      return ((StructuredTree)inputElement).children
                        .toArray(this.templateArray);
                    }
                    return null;
                  }
              
                  public Object[] getChildren(Object parentElement)
                  {
                    if ((parentElement instanceof StructuredTree)) {
                      return ((StructuredTree)parentElement).children
                        .toArray(this.templateArray);
                    }
                    return null;
                  }
              
                  public Object getParent(Object element)
                  {
                    if ((element instanceof StructuredTree)) {
                      return ((StructuredTree)element).parent;
                    }
                    return null;
                  }
              
                  public boolean hasChildren(Object element)
                  {
                    if ((element instanceof StructuredTree)) {
                      return !((StructuredTree)element).children.isEmpty();
                    }
                    return false;
                  }
              
                  public void dispose()
                  {
                  }
              
                  public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
                  {
                  }
              
                  private StructuredTree(String label, StructuredTree parent)
                  {
                    this(label);
                    this.parent = parent;
                  }
              
                  StructuredTree(DataSet dataSet, OutputProperties outputProperties) {
                    this(dataSet.getLabel());
              
                    Variant[] variants = dataSet.getVariants();
                    if (variants.length > 0)
                      for (Variant variant : variants)
                      {
                        StructuredTree newParent;
                        if (variants.length > 1) {
                           newParent = new StructuredTree(variant.getLabel());
                          addBox(newParent);
                        } else {
                          newParent = this;
                        }
              
                        Iterator ids = outputProperties
                          .getStructuredFieldsToDisplay()
                          .getAllIDsToDisplay();
              
                        while (ids.hasNext()) {
                          ID id = (ID)ids.next();
                          StructuredData data = variant.getStructuredData(id);
                          if (data != null) {
                            StructuredTree tree = new StructuredTree(                              data.getDisplayName(), data.getContents(), 
                              newParent);
                            newParent.addBox(tree);
                          }
                        }
                      }
                  }
              
                  StructuredTree(String label, Map map, StructuredTree parent)
                  {
                    this(label, parent);
              
                    Iterator entries = map.entrySet()
                      .iterator();
                    while (entries.hasNext()) {
                      Map.Entry entry = (Map.Entry)entries.next();
                      String key = (String)entry.getKey();
                      Object thing = entry.getValue();
                      if ((thing instanceof Map))
                        addBox(new StructuredTree(key, (Map)thing, this));
                      else
                        addFlatThing(key, thing);
                    }
                  }
              
                  public void addBox(StructuredTree data)
                  {
                    this.children.add(data);
                  }
              
                  public void addFlatThing(String key, Object thing) {
                    if (key != null) {
                      String string = key + " = " + thing;
                      this.children.add(string);
                    }
                  }
                }
              }

